home *** CD-ROM | disk | FTP | other *** search
/ Hardcore Visual Basic 5.0 (2nd Edition) / Hardcore Visual Basic 5.0 - Second Edition (1997)(Microsoft Press).iso / Code / Sort.cls < prev    next >
Text File  |  1997-06-14  |  11KB  |  296 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4. END
  5. Attribute VB_Name = "GSort"
  6. Attribute VB_GlobalNameSpace = True
  7. Attribute VB_Creatable = True
  8. Attribute VB_PredeclaredId = False
  9. Attribute VB_Exposed = True
  10. Option Explicit
  11.  
  12. Public Enum EErrorSort
  13.     eeBaseSort = 13620  ' Sort
  14. End Enum
  15.  
  16. ' Iterative QuickSort algorithm
  17. Sub SortArray(aTarget() As Variant, Optional vFirst As Variant, _
  18.               Optional vLast As Variant, Optional helper As ISortHelper)
  19.     Dim iFirst As Long, iLast As Long
  20.     If IsMissing(vFirst) Then iFirst = LBound(aTarget) Else iFirst = vFirst
  21.     If IsMissing(vLast) Then iLast = UBound(aTarget) Else iLast = vLast
  22.     If helper Is Nothing Then Set helper = New CSortHelper
  23.     
  24. With helper
  25.     Dim iLo As Long, iHi As Long, iRand As Long, stack As New CStack
  26.     Do
  27.         Do
  28.             ' Swap from ends until first and last meet in the middle
  29.             If iFirst < iLast Then
  30.                 ' If we're in the middle and out of order, swap
  31.                 If iLast - iFirst = 1 Then
  32.                     If .Compare(aTarget(iFirst), aTarget(iLast)) > 0 Then
  33.                         .Swap aTarget(iFirst), aTarget(iLast)
  34.                     End If
  35.                 Else
  36.                     ' Split at some random point
  37.                     .Swap aTarget(iLast), _
  38.                           aTarget(MRandom.Random(iFirst, iLast))
  39.                     ' Swap high values below the split for low values above
  40.                     iLo = iFirst: iHi = iLast
  41.                     Do
  42.                         ' Find any low value larger than split
  43.                         Do While (iLo < iHi) And _
  44.                                  (.Compare(aTarget(iLo), aTarget(iLast)) <= 0)
  45.                             iLo = iLo + 1
  46.                         Loop
  47.                         ' Find any high value smaller than split
  48.                         Do While (iHi > iLo) And _
  49.                                  (.Compare(aTarget(iHi), aTarget(iLast)) >= 0)
  50.                             iHi = iHi - 1
  51.                         Loop
  52.                         ' Swap too high low value for too low high value
  53.                         If iLo < iHi Then .Swap aTarget(iLo), aTarget(iHi)
  54.                     Loop While iLo < iHi
  55.                     ' Current (iLo) is larger than split (iLast), so swap
  56.                     .Swap aTarget(iLo), aTarget(iLast)
  57.                     ' Push range markers of larger part for later sorting
  58.                     If (iLo - iFirst) < (iLast - iLo) Then
  59.                         stack.Push iLo + 1
  60.                         stack.Push iLast
  61.                         iLast = iLo - 1
  62.                     Else
  63.                         stack.Push iFirst
  64.                         stack.Push iLo - 1
  65.                         iFirst = iLo + 1
  66.                     End If
  67.                     ' Exit from inner loop to process smaller part
  68.                     Exit Do
  69.                 End If
  70.             End If
  71.             
  72.             ' If stack empty, Exit outer loop
  73.             If stack.Count = 0 Then Exit Sub
  74.             ' Else pop first and last from last deferred section
  75.             iLast = stack.Pop
  76.             iFirst = stack.Pop
  77.         Loop
  78.     Loop
  79. End With
  80. End Sub
  81.  
  82. ' QuickSort algorithm
  83. Sub SortCollection(nTarget As Collection, Optional vFirst As Variant, _
  84.                    Optional vLast As Variant, _
  85.                    Optional helper As ISortHelper)
  86.     Dim iFirst As Long, iLast As Long
  87.     If IsMissing(vFirst) Then iFirst = 1 Else iFirst = vFirst
  88.     If IsMissing(vLast) Then iLast = nTarget.Count Else iLast = vLast
  89.     If helper Is Nothing Then Set helper = New CSortHelper
  90.     
  91. With helper
  92.     Dim iLo As Long, iHi As Long, stack As New CStack
  93.     Do
  94.         Do
  95.             ' Swap from ends until first and last meet in the middle
  96.             If iFirst < iLast Then
  97.                 ' If we're in the middle and out of order, swap
  98.                 If iLast - iFirst = 1 Then
  99.                     If .Compare(nTarget(iFirst), nTarget(iLast)) > 0 Then
  100.                         .CollectionSwap nTarget, iFirst, iLast
  101.                     End If
  102.                 Else
  103.                     ' Split at some random point
  104.                     .CollectionSwap nTarget, iLast, _
  105.                                     MRandom.Random(iFirst, iLast)
  106.                     ' Swap high values below the split for low values above
  107.                     iLo = iFirst: iHi = iLast
  108.                     Do
  109.                         ' Find find any low value larger than split
  110.                         Do While (iLo < iHi) And _
  111.                                  (.Compare(nTarget(iLo), nTarget(iLast)) <= 0)
  112.                             iLo = iLo + 1
  113.                         Loop
  114.                         ' Find any high value smaller than split
  115.                         Do While (iHi > iLo) And _
  116.                                  (.Compare(nTarget(iHi), nTarget(iLast)) >= 0)
  117.                             iHi = iHi - 1
  118.                         Loop
  119.                         ' Swap too high low value for too low high value
  120.                         If iLo < iHi Then .CollectionSwap nTarget, iLo, iHi
  121.                     Loop While iLo < iHi
  122.                     ' Current (iLo) is larger than split (iLast), so swap
  123.                     .CollectionSwap nTarget, iLo, iLast
  124.                     ' Push range markers of larger part for later sorting
  125.                     If (iLo - iFirst) < (iLast - iLo) Then
  126.                         stack.Push iLo + 1
  127.                         stack.Push iLast
  128.                         iLast = iLo - 1
  129.                     Else
  130.                         stack.Push iFirst
  131.                         stack.Push iLo - 1
  132.                         iFirst = iLo + 1
  133.                     End If
  134.                     ' Exit from inner loop to process smaller part
  135.                     Exit Do
  136.                 End If
  137.             End If
  138.             
  139.             ' If stack empty, Exit outer loop
  140.             If stack.Count = 0 Then Exit Sub
  141.             ' Else pop first and last from last deferred section
  142.             iLast = stack.Pop
  143.             iFirst = stack.Pop
  144.         Loop
  145.     Loop
  146. End With
  147. End Sub
  148.  
  149. Function BSearchArray(av() As Variant, ByVal vKey As Variant, _
  150.                       iPos As Long, _
  151.                       Optional helper As ISortHelper) As Boolean
  152.     Dim iLo As Long, iHi As Long
  153.     Dim iComp As Long, iMid As Long
  154.     If helper Is Nothing Then Set helper = New CSortHelper
  155.     
  156.     iLo = LBound(av): iHi = UBound(av)
  157.     Do
  158.         iMid = iLo + ((iHi - iLo) \ 2)
  159.         iComp = helper.Compare(av(iMid), vKey)
  160.         Select Case iComp
  161.         Case 0
  162.             ' Item found
  163.             iPos = iMid
  164.             BSearchArray = True
  165.             Exit Function
  166.         Case Is > 0
  167.             ' Item is in lower half
  168.             iHi = iMid - 1
  169.             If iHi < iLo Then Exit Do
  170.         Case Is < 0
  171.             ' Item is in upper half
  172.             iLo = iMid + 1
  173.             If iLo > iHi Then Exit Do
  174.         End Select
  175.     Loop
  176.     ' Item not found, but return position to insert
  177.     iPos = iMid - (iComp < 0)
  178.         
  179. End Function
  180.  
  181. ' BSearchCollection performs a binary search on a collection and
  182. ' returns True or False depending on whether the search item is
  183. ' found. BSearchCollection also returns the index of the search
  184. ' item in iPos. If the item isn't found, iPos will contain the
  185. ' index that the item should occupy in the collection. Note that
  186. ' iPos will equal 1 if the collection is empty, and will equal
  187. ' n.Count + 1 if the search item should be inserted at the end
  188. ' of the collection.
  189. '
  190. ' The following example uses BSearchCollection to insert an item
  191. ' in sorted order:
  192. '
  193. '    Dim n as new Collection, v As Variant, iPos As Long
  194. '
  195. '    v = InputBox("Collection item to insert: ")
  196. '    ' Insert item in collection if item doesn't already exist
  197. '    If Not BSearchCollection(n, v, iPos) Then
  198. '        On Error GoTo IndexError
  199. '        ' The following line of code generates an error if the
  200. '        ' collection is empty or iPos > n.Count. In either case,
  201. '        ' the error handler adds the item to the end of the collection
  202. '        n.Add v, , iPos
  203. '    End If
  204. '
  205. '    Exit Sub
  206. 'IndexError:
  207. '    ' Item needs to be inserted at end of collection
  208. '    n.Add v
  209.  
  210. Function BSearchCollection(n As Collection, ByVal vKey As Variant, _
  211.                            iPos As Long, _
  212.                            Optional helper As ISortHelper) As Boolean
  213.     Dim iLo As Long, iHi As Long
  214.     Dim iComp As Long, iMid As Long
  215.     If helper Is Nothing Then Set helper = New CSortHelper
  216.     
  217.     ' Special case if empty collection
  218.     If n.Count = 0 Then
  219.         iPos = 1
  220.         Exit Function
  221.     End If
  222.     
  223.     iLo = 1: iHi = n.Count
  224.     Do
  225.         iMid = iLo + ((iHi - iLo) \ 2)
  226.         iComp = helper.Compare(n(iMid), vKey)
  227.         Select Case iComp
  228.         Case 0
  229.             ' Item found
  230.             iPos = iMid
  231.             BSearchCollection = True
  232.             Exit Function
  233.         Case Is > 0
  234.             ' Item is in lower half
  235.             iHi = iMid - 1
  236.             If iHi < iLo Then Exit Do
  237.         Case Is < 0
  238.             ' Item is in upper half
  239.             iLo = iMid + 1
  240.             If iLo > iHi Then Exit Do
  241.         End Select
  242.     Loop
  243.     ' Item not found, but return position to insert
  244.     iPos = iMid - (iComp < 0)
  245.     
  246. End Function
  247.  
  248. Sub ShuffleArray(av() As Variant, Optional helper As ISortHelper)
  249.     Dim iFirst As Long, iLast As Long
  250.     If helper Is Nothing Then Set helper = New CSortHelper
  251.     
  252.     iFirst = LBound(av): iLast = UBound(av)
  253.     ' Randomize array
  254.     Dim i As Long, v As Variant, iRnd As Long
  255.     For i = iLast To iFirst + 1 Step -1
  256.         ' Swap random element with last element
  257.         iRnd = MRandom.Random(iFirst, i)
  258.         helper.Swap av(i), av(iRnd)
  259.     Next
  260. End Sub
  261.  
  262. Sub ShuffleCollection(n As Collection, Optional helper As ISortHelper)
  263.     Dim iFirst As Long, iLast As Long
  264.     If helper Is Nothing Then Set helper = New CSortHelper
  265.     
  266.     iFirst = 1: iLast = n.Count
  267.     ' Randomize collection
  268.     Dim i As Long, v As Variant, iRnd As Long
  269.     For i = iLast To iFirst + 1 Step -1
  270.         ' Swap random element with last element
  271.         iRnd = MRandom.Random(iFirst, i)
  272.         helper.CollectionSwap n, i, iRnd
  273.     Next
  274. End Sub
  275.  
  276. #If fComponent = 0 Then
  277. Private Sub ErrRaise(e As Long)
  278.     Dim sText As String, sSource As String
  279.     If e > 1000 Then
  280.         sSource = App.ExeName & ".Sort"
  281.         Select Case e
  282.         Case eeBaseSort
  283.             BugAssert True
  284.        ' Case ee...
  285.        '     Add additional errors
  286.         End Select
  287.         Err.Raise COMError(e), sSource, sText
  288.     Else
  289.         ' Raise standard Visual Basic error
  290.         sSource = App.ExeName & ".VBError"
  291.         Err.Raise e, sSource
  292.     End If
  293. End Sub
  294. #End If
  295.  
  296.